#include "rsuctrl.h"
#include "rsutransgetobusysinfo.h"
#include "rsutransinitrsuonly.h"
#include <QTime>

RsuCtrl::RsuCtrl(QObject *parent) : BaseRsuCtrl(parent)
{
    connect(&m_rsuComm, &RsuConnect::newFrameArrived,
            this, &RsuCtrl::processRsuFrame);
    connect(&m_rsuComm, &RsuConnect::newFrameArrived,
            this, &RsuCtrl::newFrameArrived);
    connect(&m_rsuComm, &RsuConnect::stateChanged,
            this, &RsuCtrl::socketStateChanged);
    memset(m_gbTermNo, 0, sizeof(m_gbTermNo));

    m_nGbPsamSlot = -1;

    m_pRsuTransMgrAtInitStage=new RsuTransInitRsuOnly(this, parent);
    m_pRsuTransMgrForNormalTrans=new RsuTransGetObuSysInfo(this, parent);
    m_pCurrentRsuTransMgr=m_pRsuTransMgrAtInitStage;
}

RsuCtrl::~RsuCtrl()
{
}

void RsuCtrl::reopenRsu()
{
    if (m_sRsuHost.length()>0)
    {
        m_rsuComm.openRsu(m_sRsuHost, m_nRsuPort);
    }
}

void RsuCtrl::closeRsu()
{
    m_rsuComm.closeRsu();
}

void RsuCtrl::configRsuType(const QString &rsuType)
{
    int nRsuType;
    if (0==rsuType.compare("chenggu", Qt::CaseInsensitive))
    {
        nRsuType=m_rsuComm.RSU_TYPE_CHENGGU;
    }
    m_rsuComm.configRsuType(nRsuType);
}

int RsuCtrl::initRsu()
{
    quint8 Time[16];
    fillCurrentTime(Time);
    return m_rsuComm.initRsuRq((char *)Time,
            m_bstInterval,
            m_rsuPower,
            m_rsuChannel);
}

int RsuCtrl::getRsuInfo()
{
    return m_rsuComm.rsuInfoRq();
}

int RsuCtrl::detectObu()
{
    static quint32 n=0;
    if (0==n%100)
    {
        qDebug()<<tr("RsuCtrl::detectObu(): [%1]").arg(n);
    }
    n++; if (n>=1000000) n=0;

    if (DEV_STATUS_OK!=m_status)
    {
        // 没有初始化之前不检测OBU
        return -1;
    }

    quint8 App[16];
    memcpy(App, "\x07\x41\x87\x29\x20\x29\x00\x2B", 8);
    App[5]=ObuInfo::SYSFILE_SIZE;  // 41
    App[7]=IccInfo::DF01_0015_SIZE; // 43

    quint8 App_out[4+ObuInfo::SYSFILE_SIZE+IccInfo::DF01_0015_SIZE+8];
    memset(App_out, 0, sizeof(App_out));
    quint8 ObuCfg[ObuInfo::OBU_CFG_SIZE+8];
    memset(ObuCfg, 0, sizeof(ObuCfg));

    quint8 Time[16];
    fillCurrentTime(Time);

    // NOTE: 在测试等情况下，修改BeanconId可以调整重新检测同一OBU的时间间隔
    modifyBeaconId();
    quint8 BeaconId[8];
    memcpy(BeaconId, m_BeaconId, 4);

    return m_rsuComm.initPrimRq((char *)BeaconId, (char *)Time,
            m_rsuChannel, 1, (char *)App, 0);
}

void RsuCtrl::socketStateChanged(QAbstractSocket::SocketState socketState)
{
    // NOTE: 如果RSU电源关闭，并不会触发该事件！也不会触发connected或disconnected事件。
    if (QAbstractSocket::ConnectedState==socketState)
    {
        startTransInit();
    }
    else
    {
        m_pCurrentRsuTransMgr->stop();
        setStatus(DEV_STATUS_ERR);
    }
}

void RsuCtrl::processRsuFrame(quint8 nFrameIndex, quint8 nFrameType, const QByteArray &frame)
{
    Q_UNUSED(nFrameIndex);

    this->m_pCurrentRsuTransMgr->processRsuFrame(nFrameIndex, nFrameType, frame);
}

bool RsuCtrl::checkRsuStatus()
{
    if (DEV_STATUS_OK!=m_status)
    {
        // 仅在设备状态正常时有必要检测设备状态
        return false;
    }

    if (m_nRsuHeartBeatCheckStyle<0 || m_nRsuHeartBeatCheckStyle>2)
    {
        // 不进行心跳检测
        return false;
    }

    qint64 currentTime=QDateTime::currentMSecsSinceEpoch();
    qint64 nTimeDiff=qAbs(currentTime-m_nDevOKTime);
    if (nTimeDiff>m_nHeartBeatCheckInterval*3)
    {
        // 如果超出时间过长，视为故障，重新连接
        qDebug()<<tr("rsu[%1]: 心跳检测失败，尝试重新连接，check style[%2]")
                       .arg(this->getId())
                       .arg(m_nRsuHeartBeatCheckStyle);
        tryReconnect();
        return true;
    }

    // 记录最近一次的命令发送间隔，避免连续发送心跳指令
    static qint64 nCheckCmdSendTime;
    nTimeDiff=qMin(nTimeDiff,
                   qAbs(currentTime-nCheckCmdSendTime));

    // NOTE：如果近期通讯正常，没必要专门发送检测指令
    if (nTimeDiff>m_nHeartBeatCheckInterval)
    {
        nCheckCmdSendTime=currentTime;
        // 一些型号的天线不支持获取状态，只能以初始化作为心跳检测手段
        switch(m_nRsuHeartBeatCheckStyle)
        {
        case 0:
            getRsuInfo();
            return true;
            break;
        case 1:
            initRsu();
            return true;
            break;
        case 2:
            tryReconnect();
            return true;
            break;
        default:
            return false;
            break;
        }
    }
    return false;
}

void RsuCtrl::releaseObu(quint32 nMacId)
{
    for (int i=0; i<3; i++)
    {
        m_rsuComm.reportEventRq(nMacId, 0, 1, 0);
    }
}

void RsuCtrl::releaseCurrentObu()
{
    releaseObu(this->m_currentObu.getMacId());
}

bool RsuCtrl::getSecureRq()
{
    quint8 rnd[16];
    quint8 credentials[16];
    memset(rnd, 0, sizeof(rnd));
    memset(credentials, 0, sizeof(credentials));
    int nRlt=m_rsuComm.getSecureRq(0, 1, 1, (char *)credentials, 1, 1, 0, 16, (char *)rnd, 0, 0);
    return (0==nRlt);
}

bool RsuCtrl::processRsuInitFrame(const QByteArray &frame)
{
    // RSUStatus[1]+rlen[1]+RSUinfo(4..?)，至少6字节
    if (frame.length()<6)
    {
        qDebug()<<tr("error rsu init frame[%1]")
                        .arg(frame.toHex().data());
        return false;
    }
    int nRsuStatus=frame[0];
    int nRsuInfoLen=frame[1];
    if ((nRsuInfoLen<4) ||
        (frame.length()<(nRsuInfoLen+2)))
    {
        qDebug()<<tr("error rsu init frame[%1]")
                        .arg(frame.toHex().data());
        return false;
    }
    QByteArray baRsuInfo=frame.mid(2, nRsuInfoLen);
    memcpy(m_BeaconId, baRsuInfo.data(), 4);
    setStatus(DEV_STATUS_OK);
    emit rsuInited(nRsuStatus, baRsuInfo);
    return true;
}


/*!
 * \brief 处理Rsu信息帧
 * \param 因为搜林和成谷的返回格式略有差异，在处理时，假定成谷的信息帧中的RSUStatus=0
 */
void RsuCtrl::processRsuInfoFrame(const QByteArray &frame)
{
    // 搜林：RSUStatus[1]+rlen[1]+RSUinfo(4..?)，至少6字节
    // 成谷：rlen[1]+RSUinfo(4..?)，至少5字节
    int nIndexForDataLen;
    if (m_rsuComm.getRsuType()==m_rsuComm.RSU_TYPE_CHENGGU)
    {
        nIndexForDataLen=0;
    }

    if ((frame.length()-nIndexForDataLen)<5)
    {
        qDebug()<<tr("error rsu info frame[%1]")
                        .arg(frame.toHex().data());
        return;
    }
    int nRsuInfoLen=frame[nIndexForDataLen];
    if ((nRsuInfoLen<4) ||
        (frame.length()<(nRsuInfoLen+nIndexForDataLen+1)))
    {
        qDebug()<<tr("error rsu info frame[%1]")
                        .arg(frame.toHex().data());
        return;
    }
    QByteArray baRsuInfo=frame.mid(nIndexForDataLen+1, nRsuInfoLen);
    // memcpy(m_BeaconId, baRsuInfo.data(), 4);
    setStatus(DEV_STATUS_OK);
    emit rsuInfoGot(baRsuInfo);
}

bool RsuCtrl::processObuInitFrame(const QByteArray &frame)
{
    /*
    深圳成谷通讯数据示例：
    00 02 ca 21 2f c0 03 91 d0 00 01 c1 01 20 27 c9 bd b6 ab 37 01 00 01 01 00 37 01 02 14 20 79 81 05 20 14 10 28 21 14 10 28 28 c9 bd b6 ab 37 01 00 01 16 10 37 02 13 03 22 01 00 25 31 48 20 14 08 13 20 04  08 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ca 21 2f 10 23 00 07
       mm mm mm mm       hh ff pp ll -- -- -- -- ss.......                                                                        ii                                                                                                                                mm mm mm mm -- -- -- cc

    上海搜林通讯数据示例：
        （无卡片）
                      00 91 d0 00 01 c1 01 00 27 c9 bd b6 ab 37 01 00 01 01 00 37 01 02 14 20 79 81 05 20 14 10 28 21 14 10 28 00 c2 b3 41 39 35 33 45 50 00 00 00 00 00 00 02 ca 21 2f 10 82 00 c3
                         hh ff pp ll -- -- -- -- ss.......                                                                                                                  mm mm mm mm
    */

    int index=0;
    int nReturnStatus=frame[index];
    index++;
    if (0!=nReturnStatus)
    {
        qDebug()<<tr("error obu return status. frame[%1]")
                        .arg(frame.toHex().data());
        return false;
    }

    if (m_rsuComm.getRsuType()==m_rsuComm.RSU_TYPE_CHENGGU)
    {
        index+=4;   // mac
        index+=2;   // 神秘的c0 03
    }
    index++;    // 91-head
    index++;    // d0-INITIALIZATION.response

    // 进一步判断返回长度
    const int nObuCfgLen=7;
    if (frame.length()<index+2+nObuCfgLen)
    {
        qDebug()<<tr("error obu frame length. frame[%1]")
                        .arg(frame.toHex().data());
        return false;
    }

    int nProfile=frame[index];
    index++;
    int nAppList=frame[index];
    index++;
    int nAppLen=frame.length()-index-nObuCfgLen;
    if (nAppLen<1)
    {
        qDebug()<<tr("error obu app length. frame[%1]")
                        .arg(frame.toHex().data());
        return false;
    }
    QByteArray baApp=frame.mid(index, nAppLen);
    QByteArray baObuCfg=frame.right(nObuCfgLen);
    return processObuInfo(nProfile, nAppList, baApp, baObuCfg);
}

//bool RsuCtrl::processGetSecureFrame(const QByteArray &frame)
//{
//    int index=6;
//    const int nFileLen=32;
//    const int nAuthenticatorLen=8;
//    const int nStatusLen=1;
///*
//20151216测试发现成谷一体机返回的GetSecure帧长度大于需要返回的内容，与带控制盒的机器不同，故修改判断条件。
//0091180115012235568bdc120d17099e11d5750e6ff80b3030291d2037b3df6468bed383ef1e0000000000000000001000
//00          06                                                              38              4647
//*/
//    if (frame.length()<index+nFileLen+nAuthenticatorLen+nStatusLen)
//    {
//        qDebug()<<tr("error getSecure frame length. frame[%1]")
//                        .arg(frame.toHex().data());
//        return false;
//    }
//    QByteArray baFile=frame.mid(index, nFileLen);
//    index+=nFileLen;
//    QByteArray baAuthenticator=frame.mid(index, nAuthenticatorLen);
//    index+=nAuthenticatorLen;
//    int nStatus=frame[index];
//    emit gotSecureFile(baFile, baAuthenticator, nStatus);
//    m_currentObu.setCipheredVehInfoFile((const quint8 *)baFile.constData(), baFile.length());
//    return true;
//}

bool RsuCtrl::processObuInfo(int nProfile, int nAppList, const QByteArray &baApp, const QByteArray &baObuCfg)
{
    if ((0!=nProfile) && (1!=nProfile))
    {
        // profile应该同搜寻OBU时指定的信道号一致
        qDebug()<<tr("unexpected profile[%1] value!").arg(nProfile);
    }
    if (1!=nAppList)
    {
        qDebug()<<tr("nAppList[%1]!=1.").arg(nAppList);
    }

    if (baApp.length()<4+ObuInfo::SYSFILE_SIZE)
    {
        qDebug()<<tr("error appLen[%1], app[%2].")
                       .arg(baApp.length())
                       .arg(baApp.toHex().data());
        return false;
    }
    ObuInfo newObu;
    IccInfo newIcc;
    bool bRlt=newObu.setObuInfo((const quint8 *)baApp.data()+4, ObuInfo::SYSFILE_SIZE, (const quint8 *)baObuCfg.data());
    if (!bRlt)
    {
        qDebug()<<tr("trans obu info error, app[%1].")
                       .arg(baApp.toHex().data());
        return false;
    }

    if (newObu.hasCpuCard())
    {
        quint8 nGB0015Start=4+ObuInfo::SYSFILE_SIZE+1;
        if (baApp.length()<nGB0015Start+IccInfo::DF01_0015_SIZE)
        {
            qDebug()<<tr("error appLen[%1] with gb card, app[%2].")
                           .arg(baApp.length())
                           .arg(baApp.toHex().data());
            return false;
        }
        bRlt=newIcc.setIcc0015((const quint8 *)baApp.data()+nGB0015Start, IccInfo::DF01_0015_SIZE);
        if (!bRlt)
        {
            qDebug()<<tr("trans icc info error, app[%1].")
                           .arg(baApp.toHex().data());
            return false;
        }
    }
    setStatus(DEV_STATUS_OK);
    m_currentObu=newObu;
    m_currentIcc=newIcc;
    if (m_nGbPsamSlot<0)
    {
        emit obuDetected(newObu, newIcc);
    }
    return true;
}

void RsuCtrl::startTransInit()
{
    m_pCurrentRsuTransMgr->stop();
    m_pCurrentRsuTransMgr=m_pRsuTransMgrAtInitStage;
    m_pCurrentRsuTransMgr->start();
}

void RsuCtrl::transToNormalTransStatus()
{
    m_pCurrentRsuTransMgr->stop();
    m_pCurrentRsuTransMgr=m_pRsuTransMgrForNormalTrans;
    m_pCurrentRsuTransMgr->start();
}

bool RsuCtrl::resumeNormalTrans()
{
    if (m_pCurrentRsuTransMgr==m_pRsuTransMgrForNormalTrans)
    {
        m_pCurrentRsuTransMgr->start();
        return true;
    }
    return false;
}

bool RsuCtrl::pauseNormalTrans()
{
    if (m_pCurrentRsuTransMgr==m_pRsuTransMgrForNormalTrans)
    {
        m_pCurrentRsuTransMgr->stop();
        return true;
    }
    return false;
}




